package com.mihuspring.www.core;

import com.mihuspring.www.core.annotation.Autowired;

import java.lang.reflect.*;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;

public class BeanDefinition {
  private Method beanStructureMethod;
  private Object beanConfig;
  private String scope = BeanScope.singleton.name();
  private Object bean;
  private String beanName;
  private Constructor constructor;
  private boolean autoComponent = false;
  private Class<?> type;
  static HashSet<BeanDefinition> beanDefinitionSet = new HashSet<>();
  public BeanDefinition(Object beanConfig, Method beanStructureMethod){
    this.beanStructureMethod = beanStructureMethod;
    this.type = beanStructureMethod.getReturnType();
    this.beanConfig = beanConfig;
    beanDefinitionSet.add(this);
  }
  public BeanDefinition(Constructor constructor){
    this.constructor = constructor;
    this.type = constructor.getDeclaringClass();
    this.autoComponent = true;
    beanDefinitionSet.add(this);
  }

  public Class<?> getType() {
    return type;
  }

  public void setBeanName(String beanName){
    this.beanName = beanName;
  }
  public void setScope(String scope){
    this.scope = scope;
  }
  public Object getBean(){
    if(scope.equals(BeanScope.singleton.name())){
      if(bean == null){
        synchronized (this){
          if(bean == null){
            bean = doGetBean();
          }
          return bean;
        }
      }else {
        return bean;
      }
    }else {
      return doGetBean();
    }
  }

  public Object getBean(Class<?> cla) {
    return beanConfig;
  }

  public Object doGetBean () {
    Object newBean;
    if(!autoComponent){
      try{
        newBean = beanStructureMethod.invoke(beanConfig);
      }catch (Exception err){
        throw new RuntimeException(err);
      }
    }else {
      try{
        newBean = constructor.newInstance();
      }catch (Exception err){
        throw new RuntimeException(err);
      }
      Field[] fields = newBean.getClass().getDeclaredFields();
      for(Field field : fields){
        if(field.isAnnotationPresent(Autowired.class)){
          Class<?> fieldType = field.getType();
          for(BeanDefinition beanDefinition : beanDefinitionSet){
            if(beanDefinition.getType() == fieldType){
              field.setAccessible(true);
              try{
                field.set(newBean, beanDefinition.getBean());
              }catch (Exception err){
                throw new RuntimeException(err);
              }
              break;
            }
          }
        }
      }
    }
    return newBean;
  }
}
